package ch.zbw.pra.sysventory.presentation.panels;

import java.util.List;

import ch.zbw.pra.sysventory.application.controller.ScanController;
import ch.zbw.pra.sysventory.model.Computer;
import ch.zbw.pra.sysventory.model.HardDisk;
import ch.zbw.pra.sysventory.model.Nic;
import ch.zbw.pra.sysventory.model.OperatingSystem;
import ch.zbw.pra.sysventory.model.Printer;
import ch.zbw.pra.sysventory.model.Processor;
import ch.zbw.pra.sysventory.model.Scanversion;
import ch.zbw.pra.sysventory.model.Software;
import ch.zbw.pra.sysventory.presentation.handler.ScanHandler;
import ch.zbw.pra.sysventory.utils.DateUtils;
import ch.zbw.pra.sysventory.utils.HostFinder;

import com.google.common.net.InetAddresses;
import com.vaadin.data.Item;
import com.vaadin.data.util.HierarchicalContainer;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.event.ItemClickEvent.ItemClickListener;
import com.vaadin.terminal.Sizeable;
import com.vaadin.terminal.ThemeResource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.AbstractSelect;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.NativeButton;
import com.vaadin.ui.Panel;
import com.vaadin.ui.ProgressIndicator;
import com.vaadin.ui.Select;
import com.vaadin.ui.Tree;
import com.vaadin.ui.VerticalLayout;

/**
 * Painted das ScanPanel
 * 
 * @author Ralph Koster
 */

public class ScanPanel extends VerticalLayout implements Button.ClickListener {

	private static final long serialVersionUID = -7951848328574242084L;
	private ApplicationPanel applicationPanel;
	private ScanController scanController;
	private HorizontalLayout titlePanel;
	private HorizontalLayout mainPanel;
	private VerticalLayout scanProcessPanel;
	private Panel scanComponentsPanel;
	private HorizontalLayout errorTextPanel;
	private VerticalLayout progressBarPanel;
	private HorizontalLayout subnetPanel;
	private HorizontalLayout errorLabelPanel;
	private Label scanStatusText;
	private ProgressIndicator progressBar;
	private NativeButton scanButton;
	private Select subnetDropdown;
	private Tree scanversionTree;
	private HierarchicalContainer container;
	private Item item;
	private int itemId;
	private int parentItemId;

	public ScanPanel(ApplicationPanel applicationPanel) {
		this.applicationPanel = applicationPanel;
		this.scanController = new ScanController();
		this.titlePanel = new HorizontalLayout();

		this.setStyleName("home-panel");
		this.setWidth(100, Sizeable.UNITS_PERCENTAGE);
		this.setHeight(100, Sizeable.UNITS_PERCENTAGE);

		this.mainPanel = new HorizontalLayout();

		mainPanel.setWidth(100, Sizeable.UNITS_PERCENTAGE);
		mainPanel.setHeight(100, Sizeable.UNITS_PERCENTAGE);
		mainPanel.setStyleName("mainPanel");

		paintTitlePanel();
		paintScanProcessPanel();
		paintScanComponentsPanel();

		this.mainPanel.addComponent(scanProcessPanel);
		this.mainPanel.addComponent(scanComponentsPanel);

		mainPanel.setExpandRatio(scanProcessPanel, 1);
		mainPanel.setExpandRatio(scanComponentsPanel, 2);

		this.addComponent(mainPanel);
	}

	private void paintTitlePanel() {
		titlePanel.setWidth(100, Sizeable.UNITS_PERCENTAGE);
		titlePanel.setSpacing(true);
		titlePanel.addStyleName("titlePanel");

		Label title = new Label(
				"Sysventory Hard- und Software Inventarisierung - Scanning");
		title.setSizeUndefined();
		title.addStyleName("h1");

		titlePanel.addComponent(title);
		titlePanel.setComponentAlignment(title, Alignment.MIDDLE_CENTER);
		titlePanel.setExpandRatio(title, 1);

		this.setWidth(100, Sizeable.UNITS_PERCENTAGE);
		this.setHeight(100, Sizeable.UNITS_PERCENTAGE);
		this.setStyleName("scan-panel");
		this.addComponent(titlePanel);
	}

	private void paintScanProcessPanel() {
		this.scanProcessPanel = new VerticalLayout();

		scanProcessPanel.setCaption("Scannen");
		scanProcessPanel.setStyleName("scanProcessPanel");
		scanProcessPanel.setSpacing(true);
		scanProcessPanel.setWidth(97, Sizeable.UNITS_PERCENTAGE);
		scanProcessPanel.setHeight(97, Sizeable.UNITS_PERCENTAGE);

		paintErrorTextPanel();
		paintSubnetPanel();
		paintProgressBarPanel();

		scanProcessPanel.addComponent(subnetPanel);
		scanProcessPanel.addComponent(errorTextPanel);
		scanProcessPanel.addComponent(progressBarPanel);
	}

	private void paintErrorTextPanel() {
		// errorTextPanel
		errorTextPanel = new HorizontalLayout();
		errorTextPanel.setSizeFull();
		errorTextPanel.setHeight("50px");
		scanProcessPanel.addComponent(errorTextPanel);

		errorLabelPanel = new HorizontalLayout();
		errorLabelPanel.setSizeFull();
		errorLabelPanel.setStyleName("errorLabelPanel");

		// icon Label
		Label iconLabel = new Label();
		iconLabel.setIcon(new ThemeResource("img/arrow.png"));

		// error Label
		Label errorLabel = new Label();
		errorLabel.setValue("Bitte wählen Sie eine Subnetzmaske aus!!");
		errorLabel.addStyleName("error-h4");

		errorLabelPanel.addComponent(iconLabel);
		errorLabelPanel.addComponent(errorLabel);

		errorLabelPanel.setExpandRatio(iconLabel, 1);
		errorLabelPanel.setExpandRatio(errorLabel, 10);
	}

	private void paintSubnetPanel() {
		// subnet panel
		subnetPanel = new HorizontalLayout();
		subnetPanel.setStyleName("subnetPanel");
		subnetPanel.setSizeFull();
		subnetPanel.setHeight("80px");

		// subnetDropdown
		this.subnetDropdown = new Select("Subnetzmaske");
		subnetDropdown.setStyleName("subnetDropdown");
		subnetDropdown.setImmediate(false);
		subnetDropdown.setNullSelectionAllowed(false);
		List<String> subnetMasks = HostFinder.getLocalSubnets();
		for (String subnet : subnetMasks) {
			subnetDropdown.addItem(subnet);
		}

		// scanButton
		scanButton = new NativeButton();
		scanButton.setStyleName("scanButton");
		scanButton.setCaption("Jetzt Scannen");
		scanButton.addListener(this);

		subnetPanel.addComponent(subnetDropdown);
		subnetPanel.addComponent(scanButton);
	}

	private void paintProgressBarPanel() {
		progressBarPanel = new VerticalLayout();
		progressBarPanel.setWidth(100, Sizeable.UNITS_PERCENTAGE);
		progressBarPanel.setHeight("80px");
		progressBarPanel.setStyleName("progressBarPanel");

		// progressBar
		progressBar = new ProgressIndicator();
		progressBar.setWidth("151px");
		progressBar.setHeight("18px");
		progressBar.setStyleName("progressBar");
		progressBar.setPollingInterval(500);
		progressBar.setEnabled(false);

		scanStatusText = new Label("Kein laufender Scan");
		scanStatusText.addStyleName("scanStatusText");

		progressBarPanel.addComponent(scanStatusText);
		progressBarPanel.addComponent(progressBar);
	}

	private void paintScanComponentsPanel() {
		scanComponentsPanel = new Panel();
		scanComponentsPanel.setCaption("Gescannte Objekte");
		scanComponentsPanel.setStyleName("scanComponentsPanel");
		scanComponentsPanel.setScrollable(true);
		scanComponentsPanel.setWidth(100, Sizeable.UNITS_PERCENTAGE);
		scanComponentsPanel.setHeight("500px");
	}

	private void paintScanComponentsTree(Scanversion scanversion) {
		scanComponentsPanel.removeAllComponents();

		scanversionTree = new Tree();
		scanversionTree.removeAllItems();

		paintScanversionContainer(scanversion);

		scanversionTree.setStyleName("scanversionTree");
		scanversionTree.setHeight(100, Sizeable.UNITS_PERCENTAGE);
		scanversionTree.setWidth(100, Sizeable.UNITS_PERCENTAGE);

		scanversionTree.setContainerDataSource(container);
		scanversionTree.setItemCaptionPropertyId("name");
		scanversionTree
				.setItemCaptionMode(AbstractSelect.ITEM_CAPTION_MODE_PROPERTY);
		scanversionTree.setSelectable(false);
		scanversionTree.setImmediate(true);
		scanversionTree.addListener(new ItemClickListener() {

			/**
			 * 
			 */
			private static final long serialVersionUID = 4249426231545952109L;

			@Override
			public void itemClick(ItemClickEvent event) {
				Object itemId = event.getItemId();
				if (!scanversionTree.isExpanded(itemId)
						&& scanversionTree.hasChildren(itemId)) {
					scanversionTree.expandItem(itemId);
				} else {
					scanversionTree.collapseItem(itemId);
				}
			}
		});
		scanComponentsPanel.addComponent(scanversionTree);
	}

	private void paintScanversionContainer(Scanversion scanversion) {
		item = null;
		itemId = 0;

		container = new HierarchicalContainer();
		container.addContainerProperty("name", String.class, null);
		container.addContainerProperty("icon", ThemeResource.class,
				new ThemeResource("../runo/icons/16/document.png"));

		addComputersToTree(scanversion);
	}

	private void addComputersToTree(Scanversion scanversion) {
		List<Computer> computers = scanversion.getComputers();
		for (Computer computer : computers) {
			String parent = "Hostname: " + computer.getHostNameComputer();
			String computerRam = "RAM: " + computer.getRamComputer();
			String computerSid = "Sid: " + computer.getSidComputer();
			String computerType = "Typ: " + computer.getTypeComputer();

			// Add the parent to the tree
			item = container.addItem(itemId);
			item.getItemProperty("name").setValue(parent);
			container.setChildrenAllowed(itemId, true);
			parentItemId = itemId;
			itemId++;

			String[] leafValues = new String[] { computerRam, computerSid,
					computerType };

			addLeafsToChildren(leafValues, parentItemId);

			addOperatingSystemToComputer(computer);
			addProcessorToComputer(computer);
			addHardDisksToCompupter(computer);
			addPrintersToComputer(computer);
			addNicsToComputer(computer);
			addSoftwareToComputer(computer);
		}
	}

	private void addOperatingSystemToComputer(Computer computer) {
		OperatingSystem operatingSystem = computer.getOs();

		String bsParent = "Betriebssystem";

		int bsParentItemId = addObjectParent(bsParent);

		String osName = "Name: " + operatingSystem.getNameOperatingSystem();
		String osVersion = "Version: "
				+ operatingSystem.getBuildOperatingSystem();
		String osArchitecture = "Architektur: "
				+ operatingSystem.getArchitecture32Bit64BitOperatingSystem();

		String[] childrenValues = new String[] { osName, osVersion,
				osArchitecture };

		addLeafsToChildren(childrenValues, bsParentItemId);
	}

	private void addProcessorToComputer(Computer computer) {
		Processor processor = computer.getProcessor();

		String procParent = "Prozessor";

		int procParentItemId = addObjectParent(procParent);

		String processorName = "Name: " + processor.getNameProcessor();
		String processorFamily = "Familie: " + processor.getFamilyProcessor();
		String processorCores = "Kerne: " + processor.getCoresProcessor();
		String processorClock = "Taktrate: " + processor.getClockProcessor();

		String[] childrenValues = new String[] { processorName,
				processorFamily, processorCores, processorClock };

		addLeafsToChildren(childrenValues, procParentItemId);
	}

	private void addHardDisksToCompupter(Computer computer) {
		List<HardDisk> hardDisks = computer.getHardDisks();

		String hardDiskParent = "Festplatten";

		int hardDiskParentItemId = addObjectParent(hardDiskParent);

		for (HardDisk hardDisk : hardDisks) {
			String hardDiskDriveLetter = "Name: " + hardDisk.getDriveLetter();
			String hardDiskCapacity = "Grösse: "
					+ hardDisk.getHardDiskCapacity();
			String hardDiskFreeSpace = "Freier Speicher: "
					+ hardDisk.getHardDiskFreeSpace();
			String hardDiskEfficiency = "Auslastung: "
					+ hardDisk.getHardDiskEfficiency();

			String[] leafValues = new String[] { hardDiskCapacity,
					hardDiskFreeSpace, hardDiskEfficiency };

			int childrenItemId = addChildrenToParent(hardDiskDriveLetter, hardDiskParentItemId);
			addLeafsToChildren(leafValues, childrenItemId);
		}
	}

	private void addPrintersToComputer(Computer computer) {
		List<Printer> printers = computer.getPrinters();

		String printerParent = "Drucker";

		int printerParentItemId = addObjectParent(printerParent);

		for (Printer printer : printers) {
			String printerName = "Name: " + printer.getNamePrinter();
			String printerDriverName = "Treiber: "
					+ printer.getDriverNamePrinter();
			String printerDriverVersion = "Treiber Version: "
					+ printer.getDriverVersionPrinter();

			String[] leafValues = new String[] { printerDriverName,
					printerDriverVersion };

			int childrenItemId = addChildrenToParent(printerName, printerParentItemId);
			addLeafsToChildren(leafValues, childrenItemId);
		}
	}

	private void addNicsToComputer(Computer computer) {
		List<Nic> nics = computer.getNics();

		String nicParent = "NIC";

		int nicParentItemId = addObjectParent(nicParent);

		for (Nic nic : nics) {
			String nicName = "Name: " + nic.getNameNic();
			String nicIPv4 = "IPv4: " + nic.getIpv4Nic();
			String nicIPv6 = "IPv6: " + nic.getIpv6Nic();
			String subnet = "Subnetz: " + nic.getSubnetNic();
			String macAddress = "MAC Adresse: " + nic.getMacAdressNic();
			String dhcp = "DHCP: " + nic.getDhcpNic();
			String standardGateway = "Standard Gateway: "
					+ nic.getStandardGatewayNic();

			String[] leafValues = new String[] { nicIPv4, nicIPv6, subnet,
					macAddress, dhcp, standardGateway };

			int childrenItemId = addChildrenToParent(nicName, nicParentItemId);

			addLeafsToChildren(leafValues, childrenItemId);
		}
	}

	private void addSoftwareToComputer(Computer computer) {
		List<Software> softwares = computer.getSoftwares();

		String softwareParent = "Software";

		int softwareParentItemId = addObjectParent(softwareParent);

		for (Software software : softwares) {
			String softwareName = "Name: " + software.getNameSoftware();
			String softwareVersion = "Version: "
					+ software.getVersionSoftware();
			String softwareVendor = "Hersteller: "
					+ software.getSoftwareVendor();

			String[] leafValues = new String[] { softwareVersion, softwareVendor };

			int childrenItemId = addChildrenToParent(softwareName, softwareParentItemId);
			
			addLeafsToChildren(leafValues, childrenItemId);
		}
	}

	private int addObjectParent(String parentValue) {
		item = container.addItem(itemId);
		item.getItemProperty("name").setValue(parentValue);
		container.setParent(itemId, parentItemId);
		container.setChildrenAllowed(itemId, true);
		int objectParentItemId = itemId;
		itemId++;
		return objectParentItemId;
	}
	
	private int addChildrenToParent(String value, int parentItemId) {
		item = container.addItem(itemId);
		item.getItemProperty("name").setValue(value);
		container.setParent(itemId, parentItemId);
		container.setChildrenAllowed(itemId, true);
		int childrenItemId = itemId;
		itemId++;
		return childrenItemId;
	}

	private void addLeafsToChildren(String[] leafValues, int parentItemId) {
		for (String value : leafValues) {
			item = container.addItem(itemId);
			item.getItemProperty("name").setValue(value);
			container.setParent(itemId, parentItemId);
			container.setChildrenAllowed(itemId, false);
			itemId++;
		}
	}

	@Override
	public void buttonClick(ClickEvent event) {
		String subnetMask = "";
		if (null == subnetDropdown.getValue()) {
			addErrorLabel();
			return;
		}
		subnetMask = subnetDropdown.getValue().toString();
		ScanHandler scanHandler = new ScanHandler(applicationPanel,
				scanController);
		if (isValidIpAddress(subnetMask)
				&& "JETZT SCANNEN"
						.equals(scanButton.getCaption().toUpperCase())) {
			setScanComponents();
			scanController.setSubnetMask(subnetMask);
			Thread thread = new Thread(scanHandler);
			thread.start();
		} else if ("SCAN ABBRECHEN".equals(scanButton.getCaption()
				.toUpperCase())) {
			setScanCanceledComponents();
			scanHandler.stopScan();
		}
	}

	private boolean isValidIpAddress(String subnet) {
		boolean isValid = InetAddresses.isInetAddress(subnet);
		return isValid;
	}

	private void setScanComponents() {
		progressBar.setEnabled(true);
		progressBar.setValue(0f);
		scanStatusText.setValue("Scan läuft...");
		scanButton.setCaption("Scan abbrechen");
		errorTextPanel.removeComponent(errorLabelPanel);
	}

	public void setScanFinishedComponents() {
		scanStatusText.setValue("Letzter Scan abgeschlossen am "
				+ DateUtils.getCurrentDatetimeFormatted());
		progressBar.setEnabled(false);
		scanButton.setCaption("Jetzt scannen");
	}

	private void setScanCanceledComponents() {
		scanStatusText.setValue("Letzter Scan abgebrochen am "
				+ DateUtils.getCurrentDatetimeFormatted());
		progressBar.setEnabled(false);
		scanButton.setCaption("Jetzt scannen");
	}

	private void addErrorLabel() {
		errorTextPanel.addComponent(errorLabelPanel);
	}

	public void updateGUI(Scanversion scanversion) {
		paintScanComponentsTree(scanversion);
		setScanFinishedComponents();
	}
}
